# PopCulture - A package to compute metrics on Wikipedia articles.
# Copyright (C) 2012  Nuno J. Silva
#
# This file is part of PopCulture
#
# PopCulture is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, version 3 of the License.
#
# PopCulture is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with PopCulture.  If not, see <http://www.gnu.org/licenses/>.

import difflib

def diff_score(previous_content, content):
    """
    Returns a measurement of the closeness of the given texts.
    """
    if previous_content is None or content is None:
        return ''

    s = difflib.SequenceMatcher(None, previous_content.split(), content.split())
    return 1 - s.ratio()

# with thanks to http://code.activestate.com/recipes/389640-argmin/
def closest_text_index(text, corpus):
    """
    Given a text and a corpus, return the index of the corpus entry
    that is more similar to the given text (the number of different
    words is used to assess similarity).

    """

    if [x for x in corpus if x is not None]:
        list_ = [(diff_score(x, text), i) for i, x in enumerate(corpus)]
        res = min(list_)[1]
        return res if res > 0.7 else None
    else:
        return None

def match_texts_strict(master, slave):
    """
    Given two lists of texts, a *master* and a *slave*, return a list
    giving, for each item from master, the corresponding index in
    slave.
    
    When there is no exact match, the result list entry for the text
    is "None".
    
    There should be no repetitions, as far as there are not repeated
    matches (this case wasn't, in fact, accounted for in this
    implementation).
    
    """

    slave_copy = slave[:]
    res = []
    for entry in master:
        try:
            match = slave_copy.index(entry)
            slave_copy[match] = None
        except ValueError:
            match = None
        res.append(match)
    return res

def match_texts(master, slave):
    """
    Given two lists of texts, a *master* and a *slave*, return a list
    giving, for each item from master, the corresponding index in
    slave.

    - First, exact matches are assigned (using match_texts_strict,
      which you can use if you do not want approximate matches);

    - Then, each yet-to-be-paired master text is paired with the
      closest slave text, using the amount of word changes as a
      measure of proximity;

    - Finally, when there are no more matches, the return list entry
      for any remaining text is "None".

    There should be no repetitions, as far as there are not repeated
    identical matches (this is due to the way match_texts_strict
    works).
    
    """

    # first, grab exact matches
    res = match_texts_strict(master, slave)

    filtered_slave = []
    for i, x in enumerate(slave):
        if i in res:
            filtered_slave.append(None)
        else:
            filtered_slave.append(x)
    # then, fill in the blanks
    for i, x in [(i, x) for i, x in enumerate(res) if x == None]:
        match = closest_text_index (master[i], filtered_slave)
        if match is not None:
            filtered_slave[match] = None
        res[i] = match
    return res

def permutate_list(list_, order):
    max_order = max(order)
    if max_order is None:
        max_order = -1
    new_list = [None] * (max_order + 1)
    for master_index, slave_index in enumerate(order):
        if slave_index is not None:
            new_list[slave_index] = list_[master_index]
        else:
            new_list.append(list_[master_index])
    return new_list
